﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

namespace Utility.DataSystem.Attributes
{
	#region enum attributes
	[AttributeUsage (AttributeTargets.Enum, AllowMultiple = false, Inherited = true)]
	public class FieldEnumAttribute : Attribute
	{
		public string prefix;
	}

	[AttributeUsage (AttributeTargets.Enum | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
	public class ContextFlagsAttribute : Attribute
	{
		public ContextFlagsAttribute (int flags)
		{
			Flags = flags;
		}

		public ContextFlagsAttribute (object flags) : this (Convert.ToInt32 (flags))
		{
		}

		public int Flags { get; }
	}

	[AttributeUsage (AttributeTargets.Enum | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
	public class IsContextLocalAttribute : Attribute
	{
		public IsContextLocalAttribute ()
		{
			IsContextLocal = true;
		}

		public IsContextLocalAttribute (bool isContextLocal)
		{
			IsContextLocal = isContextLocal;
		}

		public bool IsContextLocal { get; }
	}
	#endregion
	#region editable
	[AttributeUsage (AttributeTargets.Enum | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
	public class EditableAttribute : Attribute
	{
		public virtual bool Editable => true;
	}

	[AttributeUsage (AttributeTargets.Enum | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
	public class NotEditableAttribute : EditableAttribute
	{
		public override bool Editable => false;
	}
	#endregion
	#region composition
	[AttributeUsage (AttributeTargets.Field | AttributeTargets.Enum, AllowMultiple = false, Inherited = true)]
	public class CompositionAttribute : Attribute
	{
		public CompositionAttribute () : this (FieldComposition.Simple)
		{
		}

		public CompositionAttribute (FieldComposition componentFlags)
		{
			ComponentFlags = componentFlags;
		}

		public FieldComposition ComponentFlags { get; }
	}

	[AttributeUsage (AttributeTargets.Field | AttributeTargets.Enum, AllowMultiple = false, Inherited = true)]
	public class ComplexNumberAttribute : CompositionAttribute
	{
		public ComplexNumberAttribute () : base (FieldComposition.ComplexNumber)
		{
		}
	}

	[AttributeUsage (AttributeTargets.Field | AttributeTargets.Enum, AllowMultiple = false, Inherited = true)]
	public class OverrideableAttribute : CompositionAttribute
	{
		public OverrideableAttribute () : base (FieldComposition.Overrideable)
		{
		}
	}
	#endregion
	#region Field
	[AttributeUsage (AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
	public class FieldAttribute : Attribute
	{
		public FieldAttribute () { }

		public FieldAttribute (string showName, int paramCount = 0)
		{
			ShowName = showName;
			ParamCount = paramCount;
		}

		public string ShowName { get; private set; } = null;

		public virtual Type Type => typeof (object);
		public virtual CombineTypes CombineType => CombineTypes.Override;

		public virtual object InitValue
		{
			get => null;
			set => throw new NotSupportedException ();
		}
		public virtual object Min
		{
			get => null;
			set => throw new NotSupportedException ();
		}
		public virtual object Max
		{
			get => null;
			set => throw new NotSupportedException ();
		}

		public int ParamCount { get; }

		//utility
		protected void AssertIsNullOrType (object obj, Type type)
		{
			if (obj != null && obj.GetType () != type)
			{
				throw new ArgumentException ($"Type must be null or {type}");
			}
		}

		protected void AssertIsNullOrType (object obj, params Type[] types)
		{
			bool ok = obj == null;
			var objType = obj.GetType ();
			foreach (var type in types)
			{
				ok |= objType == type;
			}
			if (!ok)
			{
				string msg = "Type must be null ";
				foreach (var type in types)
				{
					msg += $"or {type}";
				}
				throw new ArgumentException (msg);
			}
		}

		protected void AssertIsNullOrType<T> (object obj)
		{
			AssertIsNullOrType (obj, typeof (T));
		}

		protected void AssertIsNullOrType<T1, T2> (object obj)
		{
			AssertIsNullOrType (obj, typeof (T1), typeof (T2));
		}
	}

	public class NumberAttribute : FieldAttribute
	{
		public NumberAttribute () { }

		public NumberAttribute (string showName, int paramCount = 0) : base (showName, paramCount) { }

		public override Type Type => typeof (decimal);

		public CombineTypes combineType = CombineTypes.Add;
		public override CombineTypes CombineType => combineType;

		object _initValue = 0;
		public override object InitValue
		{
			get => _initValue;
			set
			{
				_initValue = value == null ? (object)null : Convert.ToDecimal (value);
			}
		}

		object _min;
		public override object Min
		{
			get => _min;
			set
			{
				_min = value == null ? (object)null : Convert.ToDecimal (value);
			}
		}

		object _max;
		public override object Max
		{
			get => _max;
			set
			{
				_max = value == null ? (object)null : Convert.ToDecimal (value);
			}
		}
	}

	public class BoolAttribute : FieldAttribute
	{
		public BoolAttribute () { }

		public BoolAttribute (string showName, int paramCount = 0) : base (showName, paramCount) { }

		public override Type Type => typeof (bool);

		public CombineTypes combineType = CombineTypes.Or;
		public override CombineTypes CombineType => combineType;

		object _initValue = false;
		public override object InitValue
		{
			get => _initValue;
			set
			{
				AssertIsNullOrType<bool> (value);
				_initValue = value;
			}
		}
	}

	public class EnumAttribute : FieldAttribute
	{
		Type type;

		public EnumAttribute (Type type)
		{
			this.type = type;
		}

		public EnumAttribute (string showName, Type type, int paramCount = 0) : base (showName, paramCount)
		{
			this.type = type;
		}

		public override Type Type => type;

		object _initValue;
		public override object InitValue
		{
			get => _initValue;
			set
			{
				AssertIsNullOrType (value, type);
				_initValue = value;
			}
		}
	}

	public class StringAttribute : FieldAttribute
	{
		public StringAttribute () { }
		public StringAttribute (string showName, int paramCount = 0) : base (showName, paramCount) { }

		public override Type Type => typeof (string);

		object _initValue = "";
		public override object InitValue
		{
			get => _initValue;
			set
			{
				AssertIsNullOrType<string> (value);
				_initValue = value;
			}
		}
	}

	public class ObjectAttribute : FieldAttribute
	{
		public ObjectAttribute (Type type)
		{
			_type = type;
		}
		public ObjectAttribute (string showName, Type type, int paramCount = 0) : base (showName, paramCount)
		{
			_type = type;
		}

		Type _type;
		public override Type Type => _type;

		object _initValue = "";
		public override object InitValue
		{
			get => _initValue;
			set
			{
				AssertIsNullOrType (value, _type);
				_initValue = value;
			}
		}
	}
	#endregion
	#region cache
	[AttributeUsage (AttributeTargets.Field | AttributeTargets.Enum, AllowMultiple = false, Inherited = true)]
	public class AllowCacheAttribute : Attribute
	{
		public AllowCacheAttribute (bool allowCache)
		{
			AllowCache = allowCache;
		}
		public AllowCacheAttribute () : this (true)
		{
		}
		public bool AllowCache { get; private set; }
	}

	#endregion
	#region debug
	public class DebugAttribute : Attribute
	{
		public DebugAttribute () : this (1)
		{
		}

		public DebugAttribute (int debugMode)
		{
			this.DebugMode = debugMode;
		}

		public int DebugMode { get; }
	}
	#endregion
	#region event


	[AttributeUsage (AttributeTargets.Enum | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
	public class AllowedContextFlagsAttribute : Attribute

	{
		public AllowedContextFlagsAttribute (int flags)
		{
			Flags = flags;
		}

		public AllowedContextFlagsAttribute (object flags) : this (Convert.ToInt32 (flags))
		{
		}

		public int Flags { get; }
	}

	[AttributeUsage (AttributeTargets.Enum, AllowMultiple = false, Inherited = true)]
	public class EventEnumAttribute : Attribute
	{
		public string prefix;
	}

	[AttributeUsage (AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
	public class EventAttribute : Attribute
	{
		public EventAttribute (string showName)
		{
			ShowName = showName;
		}

		public string ShowName { get; }
	}
	#endregion
	#region action
	[AttributeUsage (AttributeTargets.Enum, AllowMultiple = false, Inherited = true)]
	public class ActionEnumAttribute : Attribute
	{
		public string prefix;
	}

	[AttributeUsage (AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
	public class ActionAttribute : Attribute
	{
		public ActionAttribute (string showName, int paramCount = 0)
		{
			ShowName = showName;
			ParamCount = paramCount;
		}

		public string ShowName { get; }
		public int ParamCount { get; }
	}

	[AttributeUsage (AttributeTargets.Field, AllowMultiple = true, Inherited = true)]
	public class ParamAttribute : Attribute
	{
		public ParamAttribute (int index, string showname = null, Type type = null, object initValue = null)
		{
			Index = index;
			ShowName = showname;
			Type = type ?? typeof (float);
			InitValue = initValue;
		}

		public int Index { get; }
		public Type Type { get; }
		public string ShowName { get; }

		public object InitValue { get; }
	}
	#endregion
}